{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "# 对象和类\n",
    "\n",
    "本章我们学习如何使用自定义的数据结构：**对象**。\n",
    "\n",
    "## 什么是对象\n",
    "\n",
    "“对象是 Python 中对数据的一种抽象，Python程序中所有数据都是通过对象或对象之间的关系来表示的。”\n",
    "\n",
    "在 Python 中，所有的对象都具有**id**、**type**、**value**三个属性：\n",
    "\n",
    "其中**id**代表内存地址，可以通过内置函数`id()`查看，而**type**表示对象的类别，不同的类别意味着该对象拥有的属性和方法等，可以通过`type()`方法查看。\n",
    "\n",
    "对象既包含数据（attribute，称为特性），也包含代码（函数，也称为方法），它是某一类具体事物的特殊实例。\n",
    "\n",
    "对象作为 Python 中的基本单位，可以被创建、命名或删除。Python中一般不需要手动删除对象，其垃圾回收机制会自动处理不再使用的对象，当然如果需要，也可以使用`del`语句删除某个变量；所谓命名则是指给对象贴上一个名字标签，方便使用，也就是声明或赋值变量。\n",
    "\n",
    "对于一些 Python 内置类型的对象，通常可以使用特定的语法生成，例如**数字**直接使用阿拉伯数字字面量，**字符串**使用引号`''`，**列表**使用`[]`，**字典**使用`{}`，**函数**使用`def`语法等，这些对象的类型都是 Python 内置的。\n",
    "\n",
    "当你想创建一个其他类型的对象时，首先必须定义一个类，用以指明该类型的对象所包含的内容（特性和方法）。\n",
    "\n",
    "## 类与实例\n",
    "\n",
    "在Python中通常使用`class`语句来定义一个类（类对象），与其他对象不同的是，`class`定义的对象（类）可以用于产生新的对象（实例）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "'a'.replace()\n",
    "'b'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4458100216 <class '__main__.Person'>\n"
     ]
    }
   ],
   "source": [
    "class Person():\n",
    "    pass\n",
    "\n",
    "def who(obj):\n",
    "    print(id(obj), type(obj))\n",
    "    \n",
    "someone = Person()\n",
    "who(someone)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "type"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(Person)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "上面的例子中`Person`是我们创建的一个新的类，通过调用`Person()`可以获得一个`Person`类型的实例对象，将其赋值为`someone`，就成功创建了一个与所有内置对象类型不同的对象`someone`，它的类型为`__main__.Person`。到这里，可以将Python中一切的对象分为两种：\n",
    "\n",
    "1. 可以用来生成新对象的类，包括内置的`int`、`str`以及上面定义的`Person`等；\n",
    "2. 由类生成的实例对象，包括内置类型的数字、字符串、以及上面定义的类型为`__main__.Person`的`someone`。\n",
    "\n",
    "在实践中不得不考虑的一些细节性问题：\n",
    "\n",
    "1. 需要一些方便的机制来实现面向对象编程中的继承、重载等特性；\n",
    "2. 需要一些固定的流程让我们可以在生成实例化对象的过程中执行一些特定的操作。\n",
    "\n",
    "## 类的方法\n",
    "\n",
    "在类的内部，使用`def`关键字可以为类定义一个方法，与一般函数定义不同，方法必须包含参数`self`，且为第一个参数。\n",
    "\n",
    "Python使用特殊的对象初始化方法（构造函数，在生成对象时调用）`__init__`，当创建了这个类的实例时就会调用该方法，与C++中构造函数类似。如果想重新定义一下上面的`Person`类，比如为其添加一个`name`参数，可以这样："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Person():\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "\n",
    "perter = Person('Perter')\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.Person object at 0x109c2ca58>\n"
     ]
    }
   ],
   "source": [
    "print(perter)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Perter'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "perter.name"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面代码执行流程分析：\n",
    "1. 查看Person类的定义\n",
    "2. 在内存中实例化（创建）一个新的对象\n",
    "3. 调用对象的'__init__'方法，将这个新创建的对象作为'self'参数传入，并将另一个参数（’Perter‘）作为'name'传入\n",
    "4. 将'name'的值存入对象\n",
    "5. 将名字'perter'与这个对象关联"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Person():\n",
    "    def __init__(self, name, age):        \n",
    "        self.name = name\n",
    "        self.age = age\n",
    "    def get_name(self):\n",
    "        return self.name\n",
    "    def set_name(self, name):\n",
    "        self.name = name\n",
    "    def get_age(self):\n",
    "        return self.age\n",
    "    def set_age(self, age):\n",
    "        self.age = age"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "lily = Person('Lily', 20)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Lily'"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lily.name"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "20"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lily.age"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Lily'"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lily.get_name()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "lily.set_name('Lucy')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Lucy'"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lily.name"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意：注意到__init__方法的第一个参数永远是self，表示创建的实例本身，因此，在__init__方法内部，就可以把各种属性绑定到self，因为self就指向创建的实例本身。\n",
    "\n",
    "有了__init__方法，在创建实例的时候，就不能传入空的参数了，必须传入与__init__方法匹配的参数，但self不需要传，Python解释器自己会把实例变量传进去。\n",
    "\n",
    "除self方法外，类的方法和普通函数没有什么区别，所以，你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "## 继承\n",
    "\n",
    "从已有类中衍生出新的类，添加或修改部分功能，能提高代码复用。使用继承得到的新类会自动获得旧类中的所有方法，而不需要进行复制。\n",
    "\n",
    "你可以在新类里面定义自己额外需要的方法，或者按照需要对继承的方法进行修改。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "class A():\n",
    "    def foo(self):\n",
    "        print('A.foo')\n",
    "class B(A):\n",
    "    def foo(self):\n",
    "        super().foo()\n",
    "        print('B.foo')\n",
    "    def bar(self):\n",
    "        print('B.bar')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A.foo\n",
      "B.foo\n"
     ]
    }
   ],
   "source": [
    "a = A()\n",
    "b = B()\n",
    "# a.foo()\n",
    "b.foo()\n",
    "# b.bar()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面代码示例展示了如何在子类中覆盖（override）父类的方法，在子类中如何调用父类的方法，以及在子类中添加父类中没有的新方法。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 使用super( )从父类得到帮助\n",
    "例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Person():\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "    \n",
    "class EmailPerson(Person):\n",
    "    def __init__(self, name, email):\n",
    "        super().__init__(name)\n",
    "        self.email = email\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "lilei = EmailPerson('lilei', 'lilei@aaa.com')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'lilei'"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lilei.name"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'lilei@aaa.com'"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lilei.email"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 在类中封装属性名\n",
    "\n",
    "Python中类的所有特性都是公开的，Python程序员不去依赖语言特性去封装数据，而是通过遵循一定的属性和方法命名规约来达到这个效果。\n",
    "\n",
    "第一个约定是任何以单下划线`_`开头的名字都应该是内部实现。\n",
    "\n",
    "例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [],
   "source": [
    "class A():\n",
    "    def __init__(self):\n",
    "        self.__internal = 0\n",
    "    def _internal_method(self):\n",
    "        pass\n",
    "    def set_interval(self):\n",
    "        self.__internal += 1\n",
    "        return self.__internal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = A()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.set_interval()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Python并不会真的阻止别人访问内部名称。但是如果你这么做肯定是不好的，可能会导致脆弱的代码。同时还要注意到，使用下划线开头的约定同样适用于模块名和模块级别函数。\n",
    "\n",
    "另外，使用双下划线`__`开始会导致访问名称变成其他形式（名称改写／name mangling）。\n",
    "\n",
    "例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student():\n",
    "    def __init__(self, name, score):\n",
    "        self.__name = name\n",
    "        self.__score = score\n",
    "        \n",
    "        \n",
    "    def print_score(self):\n",
    "        print('%s: %s' % (self.__name, self.__score))\n",
    "        \n",
    "    def get_name(self):\n",
    "        return self.__name\n",
    "    \n",
    "    def set_score(self, score):\n",
    "        if 0 <= score <=100:\n",
    "            self.__score = score\n",
    "        else:\n",
    "            print('bad score')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [],
   "source": [
    "bart = Student('Bart', 59)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Bart: 59\n"
     ]
    }
   ],
   "source": [
    "bart.print_score()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Bart'"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bart.get_name()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Bart'"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bart._Student__name"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "上面的类B中，私有特性和方法会被重命名为`_B__private`和`_B__private_method`。这样做的目的就是为了继承时无法被覆盖。例如："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "上面示例中，私有名称`__private`和`__private_method`会被重命名为`_C__private`和`_C__private_method`，这个跟父类B中的名称是完全不同的。\n",
    "\n",
    "上面提到的两种不同的编码约定（单下划线和双下划线）来命名私有属性应该选择哪一种？大多数而言，你应该让你的非公共名称以单下划线开头。但是，如果你清楚你的代码会涉及到子类，并且有些内部属性应该在子类中隐藏起来，那么你需要考虑使用双下划线方案。\n",
    "\n",
    "还有一点，有时候你定义的一个变量和某个保留关键字冲突，这时候可以使用单下划线作为后缀："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面并不使用单下划线前缀的原因是它避免误解它的使用初衷（如使用单下划线前缀的目的是为了防止命名冲突而不是指明这个属性是私有的）。通过使用单下划线后缀可以解决这个问题。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "## 方法类型\n",
    "\n",
    "在类中定义的方法有3种，分别是：\n",
    "\n",
    "* **实例方法**（instance method），以`self`作为第一个参数，当其被调用时，Python会把调用该方法的对象作为`self`参数传入。\n",
    "* **类方法**（class method），用`@classmethod`装饰器指定，第一个参数是类本身，通常写作`cls`，作用于整个类，对类作出的任何改变会对它的所有实例对象产生影响。\n",
    "* **静态方法**（static method），用`@staticmethod`修饰，其参数既不需要`self`，也不需要`cls`，其功能既不影响类也不影响类的实例，仅仅为了组织代码的方便。\n",
    "\n",
    "例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [],
   "source": [
    "class A():\n",
    "    count = 0\n",
    "    def __init__(self):\n",
    "        A.count += 1\n",
    "    \n",
    "    @classmethod\n",
    "    def kid(cls):\n",
    "        print('A has', cls.count, 'little objects')\n",
    "    \n",
    "    @staticmethod\n",
    "    def static_method():\n",
    "        print('Static method called!')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [],
   "source": [
    "easy_a = A()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [],
   "source": [
    "breezy_a = A()\n",
    "wheezy_a = A()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A has 3 little objects\n"
     ]
    }
   ],
   "source": [
    "easy_a.kid()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "easy_a.static_method()\n",
    "A.static_method()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 鸭子类型\n",
    "\n",
    "在程序设计中，鸭子类型（duck typing）是动态类型的一种风格。在这种风格中，一个对象有效的语义，不是由继承自特定的类或实现特定的接口，而是由\"当前方法和属性的集合\"决定。支持“鸭子类型”的语言的解释器/编译器会在解释或编译时推断对象的类型。在鸭子类型中，关注的不是对象的类型本身，而是它是如何使用的。例如，在不使用鸭子类型的语言中，我们可以编写一个函数，它接受一个类型为“鸭子”的对象，并调用它的“走”和“叫”方法。在使用鸭子类型的语言中，这样的一个函数可以接受一个任意类型的对象，并调用它的“走”和“叫”方法。如果这些需要被调用的方法不存在，那么将引发一个运行时错误。任何拥有这样的正确的“走”和“叫”方法的对象都可被函数接受的这种行为引出了以上表述，这种决定类型的方式因此得名。\n",
    "\n",
    "在动态语言设计中，可以解释为无论一个对象是什么类型的，只要它具有某类型的行为（方法），则它就是这一类型的实例，而不在于它是否显示的实现或者继承。比如，如果一个对象具备迭代器所具有的所有行为特征，那它就是迭代器了。而如何保证一个对象实现某一种类型的所有特征，则依靠**协议**。\n",
    "\n",
    "例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Que():\n",
    "    def __init__(self, person, words):\n",
    "        self.person = person\n",
    "        self.words = words\n",
    "    \n",
    "    def who(self):\n",
    "        return self.person\n",
    "    \n",
    "    def says(self):\n",
    "        return self.words + '.'\n",
    "\n",
    "class QQue(Que):\n",
    "    def says(self):\n",
    "        return self.words + '?'\n",
    "\n",
    "class EQue(Que):\n",
    "    def says(self):\n",
    "        return self.words + '!'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {},
   "outputs": [],
   "source": [
    "h1 = Que('Elmer', 'hello')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Elmer says hello.\n"
     ]
    }
   ],
   "source": [
    "print(h1.who(), 'says', h1.says())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Bunny says hi?\n"
     ]
    }
   ],
   "source": [
    "h2 = QQue('Bunny', 'hi')\n",
    "print(h2.who(), 'says', h2.says())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Daffy says haha!\n"
     ]
    }
   ],
   "source": [
    "h3 = EQue('Daffy', 'haha')\n",
    "print(h3.who(), 'says', h3.says())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [],
   "source": [
    "def who_says(obj):\n",
    "    print(obj.who(), 'says', obj.says())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Bunny says hi?\n"
     ]
    }
   ],
   "source": [
    "who_says(h2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Bab():\n",
    "    def who(self):\n",
    "        return 'Bab'\n",
    "    def says(self):\n",
    "        return 'Biu'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "metadata": {},
   "outputs": [],
   "source": [
    "biu = Bab()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Bab says Biu\n"
     ]
    }
   ],
   "source": [
    "who_says(biu)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 特殊方法（魔法方法）\n",
    "\n",
    "特殊方法是指Python类中以双下划线`__`开头和结尾的方法，比如`__init__`，根据类的定义以及传入的参数对新创建的对象进行初始化。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "11"
      ]
     },
     "execution_count": 86,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = 3 + 8"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 87,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "'ha' == 'HA'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Word():\n",
    "    def __init__(self, text):\n",
    "        self.text = text\n",
    "    \n",
    "    def equals(self, word2):\n",
    "        return self.text.lower() == word2.text.lower()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "metadata": {},
   "outputs": [],
   "source": [
    "first = Word('ha')\n",
    "second = Word('HA')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 90,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "first.equals(second)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {},
   "outputs": [],
   "source": [
    "third = Word('biu')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 92,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "first.equals(third)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Word():\n",
    "    def __init__(self, text):\n",
    "        self.text = text\n",
    "    \n",
    "    def __eq__(self, word2):\n",
    "        return self.text.lower() == word2.text.lower()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 95,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "first == second"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 构造和初始化\n",
    "\n",
    "但是当调用`x = SomeClass()`的时候，`__init__`并不是第一个被调用的方法。实际上，还有一个叫做`__new__`的方法，来构造这个实例。然后给在开始创建时候的初始化函数来传递参数。在对象生命周期的另一端，也有一个`__del__`方法（如果`__new__`和`__init__`是对象的构造器的话，那么`__del__`就是析构器），它定义的是当一个对象进行垃圾回收时候的行为。当一个对象在删除的时需要更多的清洁工作的时候此方法会很有用，比如套接字对象或者是文件对象。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "```python\n",
    "from os.path import join\n",
    "\n",
    "class FileObject:\n",
    "    '''给文件对象进行包装从而确认在删除时关闭文件流'''\n",
    "\n",
    "    def __init__(self, filepath='~', filename='sample.txt'):\n",
    "        # 读写模式打开一个文件\n",
    "        self.file = open(join(filepath, filename), 'r+')\n",
    "\n",
    "    def __del__(self):\n",
    "        self.file.close()\n",
    "        del self.file\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "### 用于比较的魔术方法\n",
    "\n",
    "```\n",
    "__lt__(self, other)    self < other\n",
    "__le__(self, other)    self <= other\n",
    "__eq__(self, other)    self == other\n",
    "__ne__(self, other)    self != other\n",
    "__gt__(self, other)    self > other\n",
    "__ge__(self, other)    self >= other\n",
    "```\n",
    "\n",
    "举一个例子，创建一个类来表示一个词语。我们也许会想要比较单词的字典序(通过字母表)，通过默认的字符串比较的方法就可以实现，但是我们也想要通过一些其他的标准来实现，比如单词长度或者音节数量。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "### 普通算数操作符\n",
    "\n",
    "```\n",
    "__add__(self, other)      加法 +\n",
    "__sub__(self, other)      减法 -\n",
    "__mul__(self, other)      乘法 *\n",
    "__floordiv__(self, other) 整数除法 //\n",
    "__truediv__(self, other)  真除法 /\n",
    "__mod__(self, other)      取模算法 %\n",
    "__divmod___(self, other)  内置divmod()函数\n",
    "__pow__(self, other)      指数运算 **\n",
    "__lshift__(self, other)   左移 <<\n",
    "__rshift__(self, other)   右移 >>\n",
    "__and__(self, other)      按位与 &\n",
    "__or__(self, other)       按位或 |\n",
    "__xor__(self, other)      按位异或 ^\n",
    "```\n",
    "\n",
    "### 其他种类的魔法方法\n",
    "\n",
    "* `__str__(self)`  等价于`str(self)`，定义如何打印对象信息，`print()`、`str()`以及字符串格式化相关方法都会用到`__str__()`。\n",
    "* `__repr__(self)` 等价于`repr(self)`，交互式解释器适用此方法输出变量。\n",
    "* `__len__(self)` 等价于`len(self)`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 96,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.Word object at 0x109c724e0>\n"
     ]
    }
   ],
   "source": [
    "print(first)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 103,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Word():\n",
    "    def __init__(self, text):\n",
    "        self.text = text\n",
    "    \n",
    "    def __eq__(self, word2):\n",
    "        return self.text.lower() == word2.text.lower()\n",
    "    \n",
    "    def __str__(self):\n",
    "        return self.text\n",
    "    \n",
    "    def __repr__(self):\n",
    "        return 'Word(\"'+ self.text +'\")'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 104,
   "metadata": {},
   "outputs": [],
   "source": [
    "first = Word('ha')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ha\n"
     ]
    }
   ],
   "source": [
    "print(first)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 105,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Word(\"ha\")"
      ]
     },
     "execution_count": 105,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "first"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
